Day 14: 텍스트 생성 실전
LLM의 텍스트 생성 품질은 디코딩 전략에 크게 좌우됩니다. 오늘은 model.generate()의 핵심 파라미터를 하나씩 실험하며 각 전략의 차이를 체감해봅니다.
기본 텍스트 생성
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
model_name = "gpt2"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
prompt = "The future of artificial intelligence is"
input_ids = tokenizer.encode(prompt, return_tensors="pt")
# Greedy Search (기본) - 매번 확률이 가장 높은 토큰 선택
output = model.generate(input_ids, max_new_tokens=50)
print("Greedy:", tokenizer.decode(output[0], skip_special_tokens=True))
# Beam Search - 여러 후보를 동시에 탐색
output = model.generate(input_ids, max_new_tokens=50, num_beams=5, early_stopping=True)
print("Beam: ", tokenizer.decode(output[0], skip_special_tokens=True))
temperature, top_k, top_p 비교
temperature는 확률 분포의 날카로움을 조절합니다. 낮으면 결정적(보수적), 높으면 다양한(창의적) 출력을 만듭니다. top_k는 상위 k개의 토큰만, top_p는 누적 확률 p까지의 토큰만 후보로 남깁니다.
# 샘플링 활성화 + 파라미터 비교 실험
configs = [
{"temperature": 0.3, "top_p": 1.0, "top_k": 0, "label": "낮은 온도 (보수적)"},
{"temperature": 1.0, "top_p": 1.0, "top_k": 0, "label": "기본 온도"},
{"temperature": 1.5, "top_p": 1.0, "top_k": 0, "label": "높은 온도 (창의적)"},
{"temperature": 1.0, "top_p": 0.9, "top_k": 0, "label": "top_p=0.9 (nucleus)"},
{"temperature": 1.0, "top_p": 1.0, "top_k": 50, "label": "top_k=50"},
]
for cfg in configs:
output = model.generate(
input_ids,
max_new_tokens=40,
do_sample=True,
temperature=cfg["temperature"],
top_p=cfg["top_p"],
top_k=cfg["top_k"],
)
text = tokenizer.decode(output[0], skip_special_tokens=True)
print(f"[{cfg['label']}]\n{text}\n")
반복 방지와 고급 파라미터
LLM은 같은 문구를 반복하는 경향이 있습니다. repetition_penalty와 no_repeat_ngram_size로 이를 억제할 수 있습니다.
output = model.generate(
input_ids,
max_new_tokens=100,
do_sample=True,
temperature=0.8,
top_p=0.92,
top_k=50,
repetition_penalty=1.2, # 1.0이면 패널티 없음, 1.2 정도가 적당
no_repeat_ngram_size=3, # 3-gram 반복 금지
num_return_sequences=2, # 여러 결과 생성
pad_token_id=tokenizer.eos_token_id,
)
for i, seq in enumerate(output):
print(f"--- 생성 결과 {i+1} ---")
print(tokenizer.decode(seq, skip_special_tokens=True))
print()
생성 전략 요약: Greedy는 빠르지만 단조롭고, Beam Search는 품질이 높지만 느리며, Sampling+top_p 조합이 가장 많이 쓰입니다. 실무에서는 temperature=0.7, top_p=0.9, repetition_penalty=1.1 조합을 시작점으로 삼고 조정하세요.
오늘의 연습문제
- 동일한 프롬프트에 대해 temperature를 0.1, 0.5, 1.0, 1.5, 2.0으로 변경하며 생성 결과를 비교하고, 어떤 패턴이 보이는지 정리해보세요.
repetition_penalty를 1.0, 1.2, 1.5, 2.0으로 바꿔가며 반복 억제 효과를 관찰해보세요. 너무 높으면 어떤 문제가 생기나요?num_beams=5인 Beam Search와do_sample=True, top_p=0.9인 Nucleus Sampling을 같은 프롬프트에 적용하고, 결과의 일관성과 다양성을 비교해보세요.